package fun.cyhgraph.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

@Slf4j
public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     * 封装 Header（头部）、Payload（有效载荷）、Signature（签名）
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息 (JWT 载荷(Payload)中的声明（用户信息、权限等）)
     * @return
     */

    /**
     * 当调用 .signWith(SignatureAlgorithm.HS256, key) 时，JJWT 会自动在头部添加以下内容：
     * {
     * "alg": "HS256",  // 签名算法
     * "typ": "JWT"     // 令牌类型
     * }
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 1. header：（自动生成）指定签名的时候使用的签名算法，至于token名称-就是JWT

        // HS256 特点：对称加密算法，签名和验证使用同一密钥
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间。  expMillis = 当前时间 + 过期时间：计算令牌过期的时间戳
        // System.currentTimeMillis()：获取当前时间戳（毫秒）
        long expMillis = System.currentTimeMillis() + ttlMillis;
        // 时间戳转换为Date对象，用于 JWT 声明
        Date exp = new Date(expMillis);

        // JWT 构建器初始化   设置jwt的 payload(载荷) 和 signature(签名)
        // Jwts.builder()：使用 JJWT 库的构建器模式创建 JWT 生成器
        // 链式调用设置 JWT 的各个部分（Header、Payload、Signature）
        JwtBuilder builder = Jwts.builder()

                // 2. payload(载荷): 如果有私有声明，一定要先设置这个自己创建的私有的声明
                // 这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                // 载荷（Payload）设置：setClaims(claims)：将自定义声明（claims）放入 JWT 载荷
                .setClaims(claims)

                // 3. signature(签名): 设置签名使用的签名算法和签名使用的秘钥
                // signWith(算法, 密钥)：指定签名算法和密钥。  secretKey.getBytes()：将字符串密钥转为字节数组
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);
        // 至于转成base64，这个Jwt.builder会自动帮我们做的

        // 信息整合构建好，生成JWT字符串并返回
        return builder.compact();
    }


    /**
     * Token解密
     * 用于解析和验证 JWT（JSON Web Token）字符串，通过签名验证确保令牌未被篡改，并返回令牌中的载荷声明（Claims）。
     * 这是 JWT 认证流程中的关键环节，用于从令牌中提取用户信息和权限
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        log.info("来到这里校验token是否一致");
        // 创建解析器：Jwts.parser() 获取 JWT 解析器（DefaultJwtParser实例）
        Claims claims = Jwts.parser()
                // 设置签名的秘钥   传入与生成时相同的密钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))

                // 设置需要解析的jwt。  parseClaimsJws(token)：解析 JWT 并验证签名
                // 内部流程：1、拆分 JWT 为 header.payload.signature 三部分
                // 2、用 header 中的算法和传入的密钥计算签名 3、与 JWT 自带的 signature 对比，验证令牌合法性
                // 获取载荷：.getBody() 从解析结果中提取载荷（Claims）
                .parseClaimsJws(token).getBody();

        System.out.println("claims " + claims);
        // 返回解析结果
        return claims;
    }
}
